﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Web.Mvc;

namespace ExtendedMvc.Elements {
	/// <summary>
	/// Builds grid columns
	/// </summary>
	public class GridColumnBuilder<TModel> : ICollection<IGridColumnBase<TModel>>
		where TModel : class {
		private readonly List<IGridColumnBase<TModel>> _columns = new List<IGridColumnBase<TModel>>();
		private IModelViewHelper<IEnumerable<TModel>> _helper;
		private ModelMetadata _metadata;
		private Expression<Func<TModel, object>> _keyExpression;

		public GridColumnBuilder(IModelViewHelper<IEnumerable<TModel>> helper, Expression<Func<TModel, object>> keyExpression) {
			_helper = helper;
			_metadata = _helper.GetMetadataForType(typeof(TModel));
			_keyExpression = keyExpression;
		}

		private IGridColumnBase<TModel> AddDataColumn(Expression<Func<TModel, object>> expression, string memberName) {
			var column = new GridDataColumn<TModel>(expression.Compile(), memberName, _metadata);
			Add(column);
			return column;
		}

		public IGridColumnBase<TModel> ForCommands(System.Action<GridCommandBuilder<TModel>> Builder = null) {
			var column = new GridCommandColumn<TModel>(Builder, _keyExpression, _helper);
			Add(column);
			return column;
		}

		public IGridColumnBase<TModel> ForData(Expression<Func<TModel, object>> propertySpecifier) {
			var memberExpression = propertySpecifier.GetMemberExpression();
			var inferredName = memberExpression == null ? null : memberExpression.Member.Name;
			return AddDataColumn(propertySpecifier, inferredName);
		}

		public IGridColumnBase<TModel> ForData(string memberName) {
			Expression<Func<TModel, object>> expression = (m) => typeof(TModel).GetProperty(memberName).GetValue(m, null);
			return AddDataColumn(expression, memberName);
		}

		protected IList<IGridColumnBase<TModel>> Columns {
			get { return _columns; }
		}

		public IEnumerator<IGridColumnBase<TModel>> GetEnumerator() {
			return _columns.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator() {
			return GetEnumerator();
		}


		//private static Type GetTypeFromMemberExpression(MemberExpression memberExpression) {
		//    if (memberExpression == null) return null;

		//    var dataType = GetTypeFromMemberInfo(memberExpression.Member, (PropertyInfo p) => p.PropertyType);
		//    if (dataType == null) dataType = GetTypeFromMemberInfo(memberExpression.Member, (MethodInfo m) => m.ReturnType);
		//    if (dataType == null) dataType = GetTypeFromMemberInfo(memberExpression.Member, (FieldInfo f) => f.FieldType);

		//    return dataType;
		//}

		//private static Type GetTypeFromMemberInfo<TMember>(MemberInfo member, Func<TMember, Type> func) where TMember : MemberInfo {
		//    if (member is TMember) {
		//        return func((TMember)member);
		//    }
		//    return null;
		//}

		protected virtual void Add(IGridColumnBase<TModel> column) {
			_columns.Add(column);
		}

		void ICollection<IGridColumnBase<TModel>>.Add(IGridColumnBase<TModel> column) {
			Add(column);
		}

		void ICollection<IGridColumnBase<TModel>>.Clear() {
			_columns.Clear();
		}

		bool ICollection<IGridColumnBase<TModel>>.Contains(IGridColumnBase<TModel> column) {
			return _columns.Contains(column);
		}

		void ICollection<IGridColumnBase<TModel>>.CopyTo(IGridColumnBase<TModel>[] array, int arrayIndex) {
			_columns.CopyTo(array, arrayIndex);
		}

		bool ICollection<IGridColumnBase<TModel>>.Remove(IGridColumnBase<TModel> column) {
			return _columns.Remove(column);
		}

		int ICollection<IGridColumnBase<TModel>>.Count {
			get { return _columns.Count; }
		}

		bool ICollection<IGridColumnBase<TModel>>.IsReadOnly {
			get { return false; }
		}
	}
}
